import logging
import os
from typing import Dict, Any, TYPE_CHECKING, Optional, List
import pandas as pd
import numpy as np
from qtorch.events.bus import EventBus, MarketDataEvent, SignalEvent
from qtorch.core.config import ConfigManager
from qtorch.core.visualizer import Visualizer

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from qtorch.di.container import DIContainer
from qtorch.core.interfaces import IDataProvider, IStrategy, IRiskManager


class Engine:
    """重构后的核心引擎，支持配置文件"""
    
    def __init__(self, di_container: 'DIContainer', event_bus: 'EventBus', config_path: Optional[str] = None):
        self._di = di_container
        self._bus = event_bus
        
        # 加载配置
        self._config_manager = ConfigManager(config_path)
        self._load_config()
        
        # 解析依赖
        self._data_provider = self._di.resolve(IDataProvider)
        self._strategy = self._di.resolve(IStrategy)
        self._risk_manager = self._di.resolve(IRiskManager)
        
        # 初始化回测参数
        self.start_date = self._config_manager.get("engine", "start_date", "2020-01-01")
        self.end_date = self._config_manager.get("engine", "end_date", "2022-12-31")
        
        # 初始化账户参数
        self._initial_capital = self._config_manager.get("engine", "initial_capital", 1000000)
        self._commission = self._config_manager.get("engine", "commission", 0.0003)
        self._margin_ratio = self._config_manager.get("engine", "margin_ratio", 0.12)
        self._contract_multiplier = self._config_manager.get("engine", "contract_multiplier", 300)
        self._allow_short = self._config_manager.get("engine", "allow_short", True)
        
        # 初始化可视化工具
        output_dir = self._config_manager.get("output", "dir", "./output")
        self._visualizer = Visualizer(output_dir=output_dir)
        
        # 初始化结果存储
        self._backtest_results = {}
        self._benchmark_results = {}
        self._metrics = {}
        self._benchmark_metrics = {}
        
        # 设置日志
        self._setup_logging()
        
        # 注册事件处理器
        self._bus.subscribe(MarketDataEvent, self._on_market_data)
        self._bus.subscribe(SignalEvent, self._on_signal)
        
        logging.info(f"引擎初始化完成，配置：{config_path or '默认配置'}")
    
    def _load_config(self) -> None:
        """加载配置"""
        # 更新风险管理参数
        risk_config = self._config_manager.get_section("risk")
        if risk_config:
            self._di.resolve(IRiskManager).update_risk_params(
                max_position=risk_config.get("max_position", 0.1),
                max_loss=risk_config.get("max_loss", 0.05),
                max_drawdown=risk_config.get("max_drawdown", 0.2),
                volatility_limit=risk_config.get("volatility_limit", 0.03),
                correlation_threshold=risk_config.get("correlation_threshold", 0.7),
                max_concentration=risk_config.get("max_concentration", 0.3),
                max_leverage=risk_config.get("max_leverage", 1.5)
            )
    
    def _setup_logging(self) -> None:
        """设置日志"""
        log_config = self._config_manager.get_section("logging")
        if not log_config:
            return
            
        log_level = getattr(logging, log_config.get("level", "INFO"))
        log_file = log_config.get("file", "./logs/backtest.log")
        console = log_config.get("console", True)
        
        # 确保日志目录存在
        if log_file:
            os.makedirs(os.path.dirname(os.path.abspath(log_file)), exist_ok=True)
        
        # 配置日志
        handlers = []
        if log_file:
            handlers.append(logging.FileHandler(log_file))
        if console:
            handlers.append(logging.StreamHandler())
            
        logging.basicConfig(
            level=log_level,
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
            handlers=handlers
        )

    def _on_market_data(self, event: MarketDataEvent) -> None:
        """市场数据事件处理"""
        try:
            signals = self._strategy.generate_signals(event.data)
            if self._risk_manager.validate_signals(signals):
                self._bus.publish(SignalEvent(
                    strategy=type(self._strategy).__name__,
                    signals=signals
                ))
        except Exception as e:
            logging.error(f"信号生成失败: {str(e)}", exc_info=True)
            # 不在异常情况下发布未定义的信号

    def _on_signal(self, event: SignalEvent) -> None:
        """信号事件处理"""
        try:
            # 获取当前持仓
            current_positions = getattr(self, '_positions', {})
            if not hasattr(self, '_positions'):
                self._positions = {}
                self._account_balance = 1000000  # 初始资金
                self._trades_history = []
                
            # 根据信号调整持仓
            for symbol, signal in event.signals.items():
                # 记录交易
                trade_record = {
                    'timestamp': event.timestamp,
                    'symbol': symbol,
                    'signal': signal,
                    'strategy': event.strategy
                }
                
                if signal > 0 and symbol not in current_positions:
                    # 开多仓逻辑
                    self._positions[symbol] = {
                        'direction': 'long',
                        'size': 1.0,  # 默认仓位大小
                        'entry_price': self._get_latest_price(symbol),
                        'entry_time': event.timestamp
                    }
                    trade_record['action'] = 'open_long'
                    logging.info(f"开多仓: {symbol}")
                    
                elif signal < 0 and symbol in current_positions and current_positions[symbol]['direction'] == 'long':
                    # 平多仓逻辑
                    exit_price = self._get_latest_price(symbol)
                    entry_price = current_positions[symbol]['entry_price']
                    profit = (exit_price - entry_price) / entry_price
                    self._account_balance *= (1 + profit * current_positions[symbol]['size'])
                    del self._positions[symbol]
                    trade_record['action'] = 'close_long'
                    trade_record['profit'] = profit
                    logging.info(f"平多仓: {symbol}, 收益: {profit:.2%}")
                    
                elif signal < 0 and symbol not in current_positions:
                    # 开空仓逻辑（如果支持做空）
                    self._positions[symbol] = {
                        'direction': 'short',
                        'size': 1.0,  # 默认仓位大小
                        'entry_price': self._get_latest_price(symbol),
                        'entry_time': event.timestamp
                    }
                    trade_record['action'] = 'open_short'
                    logging.info(f"开空仓: {symbol}")
                    
                elif signal > 0 and symbol in current_positions and current_positions[symbol]['direction'] == 'short':
                    # 平空仓逻辑
                    exit_price = self._get_latest_price(symbol)
                    entry_price = current_positions[symbol]['entry_price']
                    profit = (entry_price - exit_price) / entry_price
                    self._account_balance *= (1 + profit * current_positions[symbol]['size'])
                    del self._positions[symbol]
                    trade_record['action'] = 'close_short'
                    trade_record['profit'] = profit
                    logging.info(f"平空仓: {symbol}, 收益: {profit:.2%}")
                    
                elif signal == 0 and symbol in current_positions:
                    # 平仓逻辑
                    exit_price = self._get_latest_price(symbol)
                    entry_price = current_positions[symbol]['entry_price']
                    if current_positions[symbol]['direction'] == 'long':
                        profit = (exit_price - entry_price) / entry_price
                    else:
                        profit = (entry_price - exit_price) / entry_price
                    self._account_balance *= (1 + profit * current_positions[symbol]['size'])
                    del self._positions[symbol]
                    trade_record['action'] = 'close_position'
                    trade_record['profit'] = profit
                    logging.info(f"平仓: {symbol}, 收益: {profit:.2%}")
                
                # 记录交易历史
                self._trades_history.append(trade_record)
                
            # 更新账户状态
            self._update_account_status()
            
        except Exception as e:
            logging.error(f"交易执行失败: {str(e)}", exc_info=True)
    
    def _get_latest_price(self, symbol: str) -> float:
        """获取最新价格"""
        try:
            # 这里应该实现获取最新价格的逻辑
            # 简化实现，实际应该从数据源获取
            return 100.0
        except Exception as e:
            logging.error(f"获取价格失败: {str(e)}")
            return 100.0  # 默认价格
    
    def _update_account_status(self) -> None:
        """更新账户状态"""
        try:
            total_value = self._account_balance
            for symbol, position in self._positions.items():
                current_price = self._get_latest_price(symbol)
                if position['direction'] == 'long':
                    position_value = position['size'] * current_price
                else:
                    # 空仓价值计算
                    position_value = position['size'] * (2 * position['entry_price'] - current_price)
                total_value += position_value
            
            logging.info(f"账户总价值: {total_value:.2f}, 持仓数量: {len(self._positions)}")
        except Exception as e:
            logging.error(f"账户状态更新失败: {str(e)}")
        
    def run(self, symbols: list) -> pd.DataFrame:
        """启动引擎并返回回测数据"""
        try:
            all_results = {}
            
            # 处理每个交易标的
            for symbol in symbols:
                logging.info(f"开始回测 {symbol}")
                
                # 获取市场数据
                market_data = self._data_provider.get_market_data(
                    symbol=symbol,
                    start_date=self.start_date,
                    end_date=self.end_date
                )
                
                if market_data is None:
                    raise ValueError(f"数据提供者返回None: {symbol}")
                
                if market_data.empty:
                    raise ValueError(f"未获取到{symbol}的有效数据")
                
                # 提取收盘价数据
                closes = market_data['close']
                
                # 发布市场数据事件
                self._bus.publish(MarketDataEvent(
                    symbol=symbol,
                    data=market_data.to_dict('records')
                ))
                
                # 存储结果
                all_results[symbol] = market_data
                self._backtest_results[symbol] = market_data
            
            # 获取基准数据
            benchmarks = self._config_manager.get("data", "benchmarks", ["sh000300"])
            if benchmarks:
                for benchmark in benchmarks:
                    try:
                        benchmark_data = self._data_provider.fetch_benchmark_data(
                            benchmark_code=benchmark,
                            start_date=self.start_date,
                            end_date=self.end_date
                        )
                        if not benchmark_data.empty:
                            self._benchmark_results[benchmark] = benchmark_data
                    except Exception as e:
                        logging.warning(f"获取基准数据失败: {benchmark}, {str(e)}")
            
            # 合并结果（如果有多个标的）
            if len(all_results) > 1:
                # 合并多个标的的结果
                combined_results = pd.concat([df for df in all_results.values()], axis=1)
                return combined_results
            elif len(all_results) == 1:
                # 返回单个标的的结果
                return list(all_results.values())[0]
            else:
                raise ValueError("没有有效的回测结果")
                
        except Exception as e:
            logging.error(f"数据加载失败: {str(e)}")
            raise

    def calculate_metrics(self, backtest_data: pd.DataFrame) -> Dict[str, float]:
        """计算回测指标"""
        import numpy as np
        
        # 确保数据包含必要的列
        if 'returns' not in backtest_data.columns:
            if 'close' in backtest_data.columns:
                # 计算收益率
                backtest_data['returns'] = backtest_data['close'].pct_change()
            else:
                raise ValueError("回测数据缺少收益率或收盘价列")
        
        # 过滤无效值
        returns = backtest_data['returns'].fillna(0)
        
        # 计算累积收益
        cum_returns = (1 + returns).cumprod()
        backtest_data['cum_returns'] = cum_returns
        
        # 基础收益指标
        total_return = cum_returns.iloc[-1] - 1
        annual_return = (1 + total_return) ** (252/len(returns)) - 1
        
        # 风险指标
        volatility = returns.std() * np.sqrt(252)  # 年化波动率
        downside_returns = returns[returns < 0]
        downside_deviation = downside_returns.std() * np.sqrt(252) if len(downside_returns) > 0 else 0
        
        # 回撤指标
        cummax_returns = cum_returns.cummax()
        drawdowns = (cummax_returns - cum_returns) / cummax_returns
        max_drawdown = drawdowns.max()
        
        # 风险调整收益
        sharpe_ratio = annual_return / volatility if volatility != 0 else 0
        sortino_ratio = annual_return / downside_deviation if downside_deviation != 0 else 0
        calmar_ratio = annual_return / max_drawdown if max_drawdown != 0 else 0
        
        # 交易统计
        if 'position' in backtest_data.columns:
            position_changes = backtest_data['position'].diff().fillna(0)
            trades = position_changes[position_changes != 0]
            num_trades = len(trades)
            
            # 计算胜率
            if 'trade_pnl' in backtest_data.columns:
                win_trades = backtest_data['trade_pnl'][backtest_data['trade_pnl'] > 0]
                win_rate = len(win_trades) / num_trades if num_trades > 0 else 0
                avg_win = win_trades.mean() if len(win_trades) > 0 else 0
                
                loss_trades = backtest_data['trade_pnl'][backtest_data['trade_pnl'] < 0]
                avg_loss = loss_trades.mean() if len(loss_trades) > 0 else 0
                
                profit_factor = abs(win_trades.sum() / loss_trades.sum()) if loss_trades.sum() != 0 else float('inf')
            else:
                win_rate = 0
                avg_win = 0
                avg_loss = 0
                profit_factor = 0
        else:
            num_trades = 0
            win_rate = 0
            avg_win = 0
            avg_loss = 0
            profit_factor = 0
        
        # 计算最大连续盈利/亏损
        if 'trade_pnl' in backtest_data.columns:
            trade_results = backtest_data['trade_pnl'] > 0
            win_streak = 0
            loss_streak = 0
            max_win_streak = 0
            max_loss_streak = 0
            current_win_streak = 0
            current_loss_streak = 0
            
            for result in trade_results:
                if result:  # 盈利
                    current_win_streak += 1
                    current_loss_streak = 0
                    max_win_streak = max(max_win_streak, current_win_streak)
                else:  # 亏损
                    current_loss_streak += 1
                    current_win_streak = 0
                    max_loss_streak = max(max_loss_streak, current_loss_streak)
        else:
            max_win_streak = 0
            max_loss_streak = 0
        
        # 计算月度/季度/年度收益
        if len(returns) > 20:  # 确保有足够的数据
            # 假设数据有日期索引
            if isinstance(backtest_data.index, pd.DatetimeIndex):
                monthly_returns = returns.resample('M').apply(lambda x: (1 + x).prod() - 1)
                quarterly_returns = returns.resample('Q').apply(lambda x: (1 + x).prod() - 1)
                yearly_returns = returns.resample('Y').apply(lambda x: (1 + x).prod() - 1)
                
                best_month = monthly_returns.max()
                worst_month = monthly_returns.min()
                best_quarter = quarterly_returns.max()
                worst_quarter = quarterly_returns.min()
                best_year = yearly_returns.max()
                worst_year = yearly_returns.min()
            else:
                # 如果没有日期索引，则无法计算这些指标
                best_month = worst_month = best_quarter = worst_quarter = best_year = worst_year = 0
        else:
            best_month = worst_month = best_quarter = worst_quarter = best_year = worst_year = 0
        
        # 保存指标
        metrics = {
            # 基础收益指标
            "总收益率": total_return,
            "年化收益率": annual_return,
            "波动率": volatility,
            "下行波动率": downside_deviation,
            
            # 回撤指标
            "最大回撤": max_drawdown,
            
            # 风险调整收益
            "夏普比率": sharpe_ratio,
            "索提诺比率": sortino_ratio,
            "卡玛比率": calmar_ratio,
            
            # 交易统计
            "交易次数": num_trades,
            "胜率": win_rate,
            "平均盈利": avg_win,
            "平均亏损": avg_loss,
            "盈亏比": abs(avg_win / avg_loss) if avg_loss != 0 else float('inf'),
            "利润因子": profit_factor,
            "最大连胜": max_win_streak,
            "最大连亏": max_loss_streak,
            
            # 周期收益
            "最佳月度收益": best_month,
            "最差月度收益": worst_month,
            "最佳季度收益": best_quarter,
            "最差季度收益": worst_quarter,
            "最佳年度收益": best_year,
            "最差年度收益": worst_year
        }
        
        # 存储指标
        self._metrics = metrics
        
        return metrics
        
    def generate_report(self, strategy_name: str = "未命名策略", output_dir: Optional[str] = None) -> None:
        """生成回测报告"""
        try:
            # 检查是否有回测结果
            if not self._backtest_results:
                logging.warning("没有回测结果，无法生成报告")
                return
                
            # 获取第一个回测结果
            symbol = list(self._backtest_results.keys())[0]
            backtest_data = self._backtest_results[symbol]
            
            # 计算指标（如果尚未计算）
            if not self._metrics:
                self._metrics = self.calculate_metrics(backtest_data)
                
            # 获取基准指标（如果有基准数据）
            benchmark_metrics = None
            benchmark_data = None
            if self._benchmark_results:
                benchmark_symbol = list(self._benchmark_results.keys())[0]
                benchmark_data = self._benchmark_results[benchmark_symbol]
                benchmark_metrics = self.calculate_metrics(benchmark_data)
                self._benchmark_metrics = benchmark_metrics
                
            # 设置输出目录
            if output_dir:
                self._visualizer.output_dir = output_dir
                
            # 生成报告
            self._visualizer.generate_report(
                backtest_data=backtest_data,
                metrics=self._metrics,
                benchmark_data=benchmark_data,
                benchmark_metrics=benchmark_metrics,
                strategy_name=strategy_name
            )
            
            logging.info(f"回测报告已生成: {self._visualizer.output_dir}")
            
        except Exception as e:
            logging.error(f"生成回测报告失败: {str(e)}", exc_info=True)
